package com.jifujun.junbatis.base.util;

import com.jifujun.junbatis.base.annotation.IgnoreColumn;
import com.jifujun.junbatis.base.annotation.TableName;
import com.jifujun.junbatis.base.exception.CatchException;
import com.jifujun.junbatis.base.exception.FrameException;
import com.jifujun.junbatis.base.service.Condition;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * Create with IntelliJ IDEA
 * Project Name: junbatis
 * Package Name: club.emergency.junbatis.base.util
 * Date: 2018/3/29 10:30
 * User: 快乐的小菌菌
 * Description: sql构建时需要的处理工具类,主要是真对mybatis识别的格式转换
 */
public class SqlUtil {

    /**
     * 获得所有的属性(符合数据库合适的字段)，并且全部驼峰转下划线 默认不是驼峰 ps:[user_name,user_age],
     * 并且支持ignore，推荐*。
     *
     * @param clazz      Class
     * @param isDeclared 是否包含父类方法 true为本类方法，false为包含父类方法
     * @param isHump     是否为驼峰
     * @return String[]
     */
    public static String[] sqlColumnBuilder(Class clazz, Boolean isDeclared, Boolean isHump) {
        Method[] methods;
        if (isDeclared) {
            methods = clazz.getDeclaredMethods();
        } else {
            methods = clazz.getMethods();
        }
        List<String> columnList = new ArrayList<>();
        for (Method method : methods) {
            if (method.getName().startsWith("get")) {
                String name = method.getName().substring(3);
                if (!name.equals("Class")) {
                    boolean ignoreColumnPresent = method.isAnnotationPresent(IgnoreColumn.class);
                    if (!ignoreColumnPresent) {
                        if (!isHump) {
                            columnList.add(SqlStringUtil.humpUnderline(name));
                        } else {
                            columnList.add(SqlStringUtil.underlineHump(name));
                        }
                    }
                }
            }
        }
        String[] strColumn = new String[columnList.size()];
        return columnList.toArray(strColumn);
    }

    /**
     * update条件生成专用
     * 获得所有的属性(符合数据库合适的字段)，并且全部驼峰转下划线 默认不是驼峰 ps:[user_name,user_age],
     * 并且支持ignore，推荐*。
     *
     * @param clazz      Class
     * @param isDeclared 是否包含父类方法 true为本类方法，false为包含父类方法
     * @param isHump     是否为驼峰
     * @return String[]
     */
    public static String[] updateColumnBuilder(Object object, Class clazz, Boolean isDeclared, Boolean isHump) {
        Method[] methods;
        if (isDeclared) {
            methods = clazz.getDeclaredMethods();
        } else {
            methods = clazz.getMethods();
        }
        List<String> columnList = new ArrayList<>();
        for (Method method : methods) {
            if (method.getName().startsWith("get")) {
                String name = method.getName().substring(3);
                if (!name.equals("Class") && !name.equals("Id")) {
                    boolean ignoreColumnPresent = method.isAnnotationPresent(IgnoreColumn.class);
                    if (!ignoreColumnPresent) {
                        Field declaredField = null;
                        try {
                            declaredField = clazz.getDeclaredField(SqlStringUtil.firstCharLowerCase(name));
                            declaredField.setAccessible(true);
                            if (Objects.nonNull(declaredField.get(object))) {
                                if (!isHump) {
                                    columnList.add(SqlStringUtil.humpUnderline(name));
                                } else {
                                    columnList.add(SqlStringUtil.underlineHump(name));
                                }
                            }
                        } catch (NoSuchFieldException e) {
                            Class superclass = clazz.getSuperclass();
                            try {
                                Field superField = superclass.getDeclaredField(SqlStringUtil.firstCharLowerCase(name));
                                superField.setAccessible(true);
                                if (Objects.nonNull(superField.get(object)) || name.equals("ModifyTime"))
                                    columnList.add(SqlStringUtil.humpUnderline(name));
                            } catch (NoSuchFieldException | IllegalAccessException e1) {
                                e1.printStackTrace();
                            }
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                            /// TODO: 5/11/18 异常捕获
                        }
                    }
                }
            }
        }
        String[] strColumn = new String[columnList.size()];
        return columnList.toArray(strColumn);
    }

    //sql构建 ps:#{userName}
    public static String[] sqlConditionColumnBuilder(Class clazz, Boolean isDeclared) {
        String[] columns = sqlColumnBuilder(clazz, isDeclared, false);
        String[] resultColumns = new String[columns.length];
        for (int i = 0; i < columns.length; i++) {
            resultColumns[i] = "#{" + SqlStringUtil.underlineHump(columns[i]) + "}";
        }
        return resultColumns;
    }

    //别名：拼接字段和别名。并且别名下划线转驼峰 ps:[user_name AS userName,user_age AS userAge]
    public static String[] sqlAliasColumnBuilder(Class clazz, Boolean isDeclared) {
        String[] columns = sqlColumnBuilder(clazz, isDeclared, false);
        String[] resultColumns = new String[columns.length];
        for (int i = 0; i < columns.length; i++) {
            resultColumns[i] = columns[i] + " AS " + SqlStringUtil.underlineHump(columns[i]);
        }
        return resultColumns;
    }

    //构建update SET关键字中的数据 ps:obj.user_name=#{userName},obj.user_age=#{userAge}
    public static String[] sqlSetOneColumnBuilder(Class clazz) {
        String[] columns = sqlColumnBuilder(clazz, false, false);
        String[] conditionColumns = sqlConditionColumnBuilder(clazz, false);
        int columnsLength = columns.length;
        int conditionColumnsLength = conditionColumns.length;
        if (columnsLength == conditionColumnsLength) {
            String[] resultColumns = new String[columnsLength];
            for (int i = 0; i < columnsLength; i++) {
                if (!columns[i].equals("id"))
                    resultColumns[i] = "obj1." + columns[i] + "=" + conditionColumns[i];
            }
            return resultColumns;
        } else {
            throw new RuntimeException(FrameException.SQL_SET_COLUMN_BUILDER_FAILED);
        }
    }

    //获得表名 selectInPageForOne、update要使用别名，其余的不需要（会有语法错误）
    public static String getTableName(Class clazz, Boolean isAlias) {
        boolean annotationPresent = clazz.isAnnotationPresent(TableName.class);
        if (annotationPresent) {
            TableName tableNameAnnotation = (TableName) clazz.getDeclaredAnnotation(TableName.class);
            String value = tableNameAnnotation.value();
            if (value.equals("")) {
                String tableName = SqlStringUtil.humpUnderline(clazz.getSimpleName());
                if (isAlias)
                    return tableName + " AS obj1";
                else
                    return tableName;
            } else {
                if (isAlias)
                    return value + " AS obj1";
                else
                    return value;
            }
        } else {
            throw new CatchException(FrameException.GET_TABLE_NAME_FAILED);
        }
    }

    /**
     * 构建查询规则，obj.iden LIKE #{arg1.C0} AND obj.student_age > #{arg1.C1}
     *
     * @param condition Condition参数
     * @param column    字段可以是驼峰也可以不是
     * @param size      Map大小 C0,C1,C2...之用
     * @param isAlias   是否启用obj别名
     * @param argIndex  ArgIndex，这里用只是为了复用，其他地方我还是硬编码
     * @return str
     */
    public static String constructorCondition(Condition condition, String column, Integer size, Boolean isAlias, String... argIndex) {
        StringBuilder stringBuilder = new StringBuilder();
        if (isAlias)
            stringBuilder.append("obj1.").append(SqlStringUtil.humpUnderline(column));
        else
            stringBuilder.append(SqlStringUtil.humpUnderline(column));
        switch (condition) {
            case EQUAL:
                stringBuilder.append(" = ");
                break;
            case IN:
                stringBuilder.append(" in ");
                break;
            case NOT_EQUAL:
                stringBuilder.append(" != ");
                break;
            case LESS:
                stringBuilder.append(" < ");
                break;
            case GREAT:
                stringBuilder.append(" > ");
                break;
            case LESS_EQUAL:
                stringBuilder.append(" <= ");
                break;
            case GREAT_EQUAL:
                stringBuilder.append(" >= ");
                break;
            case LIKE:
                stringBuilder.append(" LIKE ");
                break;
        }
        stringBuilder.append("#{").append(argIndex[0]).append(".").append(argIndex[1]).append(size).append("}");
        return stringBuilder.toString();
    }
}
